package com.emm.yixun.website.controller.workflow.process;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;

import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.UserTask;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.emm.yixun.anotations.MerchantResourcesConfig;
import com.emm.yixun.common.model.Image;
import com.emm.yixun.common.model.NodeUser;
import com.emm.yixun.common.model.User;
import com.emm.yixun.common.model.dto.NodeUserDto;
import com.emm.yixun.common.page.PageUtil;
import com.emm.yixun.common.response.Response;
import com.emm.yixun.common.response.ResponseList;
import com.emm.yixun.system.service.NodeUserServiceFacade;
import com.emm.yixun.system.service.UserServiceFacade;
import com.emm.yixun.website.common.ajax.AjaxRes;
import com.emm.yixun.website.common.mybatis.Page;
import com.emm.yixun.website.common.utils.base.Const;
import com.emm.yixun.website.common.utils.security.ShiroUtil;
import com.emm.yixun.website.controller.base.BaseController;
import com.emm.yixun.website.entity.workflow.process.ProcessDefinitionVo;
import com.emm.yixun.website.service.ao.DataService.OaDataService;
import com.emm.yixun.website.vo.process.NodeVo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

/**
 * 流程定义管理
 */
@Controller
@RequestMapping(value = "/backstage/workflow/process/")
public class ProcessController extends BaseController<Object> {

	@Autowired
	private RepositoryService repositoryService;
	@Autowired
	private NodeUserServiceFacade nodeUserServiceFacade;
	@Autowired
	private UserServiceFacade userServiceFacade;
	@Autowired
	private OaDataService oaDataService;

	/**
	 * 流程定义列表
	 */
	@RequestMapping(value = "index")
	public String index(org.springframework.ui.Model model) {
		if (doSecurityIntercept(Const.RESOURCES_TYPE_MENU)) {
			model.addAttribute("permitBtn", getPermitBtn(Const.RESOURCES_TYPE_FUNCTION));
			return "/system/workflow/process/list";
		}
		return Const.NO_AUTHORIZED_URL;
	}

	/**
	 * 查找流程定义列表
	 * 
	 * @param page
	 *            分页类
	 * @param keyWord
	 *            关键字
	 * @return
	 */
	@RequestMapping(value = "findByPage", method = RequestMethod.POST)
	@ResponseBody
	public AjaxRes findByPage(Page<ProcessDefinitionVo> page, String keyWord) {
		AjaxRes ar = getAjaxRes();
		if (ar.setNoAuth(doSecurityIntercept(Const.RESOURCES_TYPE_MENU, "/backstage/workflow/process/index"))) {
			try {
				int start = PageUtil.begin(page.getPageNum(), page.getPageSize());
				int pageSize = page.getPageSize();

				ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
				List<ProcessDefinition> processDefinitionList = new ArrayList<ProcessDefinition>();
				List<ProcessDefinitionVo> pdvoList = new ArrayList<ProcessDefinitionVo>();
				if (StringUtils.isNotBlank(keyWord)) {
					processDefinitionList = query.processDefinitionNameLike("%" + keyWord + "%").orderByDeploymentId()
							.desc().listPage(start, pageSize);
				} else {
					processDefinitionList = query.orderByDeploymentId().desc().listPage(start, pageSize);
				}
				for (ProcessDefinition pd : processDefinitionList) {
					String deploymentId = pd.getDeploymentId();
					Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId)
							.singleResult();
					ProcessDefinitionVo pdvo = new ProcessDefinitionVo(pd.getId(), deploymentId, pd.getName(),
							pd.getKey(), pd.getVersion(), deployment.getDeploymentTime(), pd.getResourceName(),
							pd.getDiagramResourceName());
					pdvoList.add(pdvo);
				}
				long count = query.count();
				page.setTotalRecord((int) count);
				page.setResults(pdvoList);
				Map<String, Object> p = new HashMap<String, Object>();
				p.put("permitBtn", getPermitBtn(Const.RESOURCES_TYPE_BUTTON));
				p.put("list", page);
				ar.setSucceed(p);
			} catch (Exception e) {
				logger.error(e.toString(), e);
				ar.setFailMsg(Const.DATA_FAIL);
			}
		}
		return ar;
	}

	/**
	 * 删除部署的流程，级联删除流程实例
	 *
	 * @param deploymentId
	 *            流程部署ID
	 */
	@RequestMapping(value = "del", method = RequestMethod.POST)
	@ResponseBody
	public AjaxRes del(String processDefinitionId) {
		AjaxRes ar = getAjaxRes();
		if (ar.setNoAuth(doSecurityIntercept(Const.RESOURCES_TYPE_BUTTON))) {
			try {
				ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
						.processDefinitionId(processDefinitionId).singleResult();
				repositoryService.deleteDeployment(pd.getDeploymentId(), true);
				ar.setSucceedMsg(Const.DEL_SUCCEED);
			} catch (Exception e) {
				logger.error(e.toString(), e);
				ar.setFailMsg(Const.DEL_FAIL);
			}
		}
		return ar;
	}

	@RequestMapping(value = "uploadModel")
	@ResponseBody
	public AjaxRes uploadModel(@RequestParam(value = "modelFile", required = false) MultipartFile file) {
		AjaxRes ar = getAjaxRes();
		if (ar.setNoAuth(doSecurityIntercept(Const.RESOURCES_TYPE_FUNCTION))) {
			try {
				String fileName = file.getOriginalFilename();
				InputStream fileInputStream = file.getInputStream();
				Deployment deployment = null;
				String extension = FilenameUtils.getExtension(fileName);
				if (extension.equals("zip") || extension.equals("bar")) {
					ZipInputStream zip = new ZipInputStream(fileInputStream);
					deployment = repositoryService.createDeployment().addZipInputStream(zip).deploy();
				} else {
					deployment = repositoryService.createDeployment().addInputStream(fileName, fileInputStream)
							.deploy();
				}
				if (deployment != null) {
					ar.setSucceedMsg("上传成功");
				}
			} catch (Exception e) {
				logger.error("error on deploy process, because of file input stream", e);
				ar.setFailMsg("上传失败");
			}
		}
		return ar;
	}

	@RequestMapping(value = "convertToModel")
	@ResponseBody
	public AjaxRes convertToModel(String processDefinitionId) {
		AjaxRes ar = getAjaxRes();
		if (ar.setNoAuth(doSecurityIntercept(Const.RESOURCES_TYPE_BUTTON))) {
			try {
				ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
						.processDefinitionId(processDefinitionId).singleResult();
				InputStream bpmnStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
						processDefinition.getResourceName());
				XMLInputFactory xif = XMLInputFactory.newInstance();
				InputStreamReader in = new InputStreamReader(bpmnStream, "UTF-8");
				XMLStreamReader xtr = xif.createXMLStreamReader(in);
				BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

				BpmnJsonConverter converter = new BpmnJsonConverter();
				com.fasterxml.jackson.databind.node.ObjectNode modelNode = converter.convertToJson(bpmnModel);
				Model modelData = repositoryService.newModel();
				modelData.setKey(processDefinition.getKey());
				modelData.setName(processDefinition.getName());
				modelData.setCategory(processDefinition.getDeploymentId());

				ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
				modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processDefinition.getName());
				modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
				modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, processDefinition.getDescription());
				modelData.setMetaInfo(modelObjectNode.toString());

				repositoryService.saveModel(modelData);
				repositoryService.addModelEditorSource(modelData.getId(), modelNode.toString().getBytes("utf-8"));
				ar.setSucceedMsg("转换成功");
			} catch (Exception e) {
				logger.error("转换模型失败", e);
				ar.setFailMsg("转换失败");
			}
		}
		return ar;
	}

	/**
	 * 读取资源，通过部署ID
	 *
	 * @param processDefinitionId
	 *            流程定义
	 * @param resourceType
	 *            资源类型(xml|image)
	 * @throws Exception
	 */
	@RequestMapping(value = "resource/read")
	public void loadByDeployment(@RequestParam("processDefinitionId") String processDefinitionId,
			@RequestParam("resourceType") String resourceType, HttpServletResponse response) throws Exception {
		response.setCharacterEncoding("UTF-8");
		ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
				.processDefinitionId(processDefinitionId).singleResult();
		String resourceName = "";
		if (resourceType.equals("image")) {
			resourceName = processDefinition.getDiagramResourceName();
		} else if (resourceType.equals("xml")) {
			resourceName = processDefinition.getResourceName();
		}
		InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
				resourceName);
		byte[] b = new byte[1024];
		int len = -1;
		while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
			response.getOutputStream().write(b, 0, len);
		}
	}

	// 跳转流程节点
	@RequestMapping(value = "toNodeList")
	@MerchantResourcesConfig(resourcesCode = "menu_0003_0001_0006")
	public String toNodeList(String processDefinitionId, String projectId, ModelMap map, HttpServletRequest request,
			HttpServletResponse response) {
		map.put("processDefinitionId", processDefinitionId);
		map.put("projectId", projectId);
		ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);
		map.put("processDefinitionName", processDefinition.getName());
		return "/system/workflow/process/nodeList";
	}

	// 查询流程节点
	@RequestMapping(value = "nodeList")
	@ResponseBody
	public AjaxRes nodeList(String processDefinitionId, String projectId) {
		User user = ShiroUtil.getCurrentUser();
		AjaxRes ar = getAjaxRes();
		BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
		List<Process> processList = bpmnModel.getProcesses();
		List<NodeVo> nodeList = new ArrayList<NodeVo>();
		for (Process process : processList) {
			// 返回该流程的所有任务，事件
			Collection<FlowElement> feList = process.getFlowElements();
			for (FlowElement f : feList) {
				if (f instanceof org.activiti.bpmn.model.UserTask) {
					UserTask userTask = (org.activiti.bpmn.model.UserTask) f;
					NodeVo nodeVo = new NodeVo();
					nodeVo.setProcessDefinitionId(processDefinitionId);
					nodeVo.setId(userTask.getId());
					nodeVo.setNodeName(userTask.getName());
					// 查询人员
					NodeUserDto nodeUserDto = new NodeUserDto();
					nodeUserDto.setMerchantId(user.getMerchantId());
					nodeUserDto.setProcessDefinitionId(processDefinitionId);
					// todo 设置项目ID
					if (StringUtils.isNotBlank(projectId))
						nodeUserDto.setProjectId(Long.valueOf(projectId));
					/* nodeUserDto.setProcessDefinitionId(processDefinitionId); */
					nodeUserDto.setNodeId(userTask.getId());
					ResponseList<NodeUser> nodeUserList = nodeUserServiceFacade.findByDto(nodeUserDto);
					if (nodeUserList.isSuccess() && null != nodeUserList.getEntity()) {
						NodeUser nodeUser = nodeUserList.getEntity().get(0);
						if (null != nodeUser) {
							nodeVo.setNodeUserId(String.valueOf(nodeUser.getUserId()));
							Response<User> userResponse = userServiceFacade.findById(nodeUser.getUserId());
							if (userResponse.isSuccess() && null != userResponse.getEntity()) {
								nodeVo.setNodeUserName(userResponse.getEntity().getUserName());
							}
							nodeVo.setNodeRoleId(nodeUser.getRemark());
						}
					}

					nodeList.add(nodeVo);
				}
			}
		}
		ar.setSucceed(nodeList);
		return ar;
	}

	// 查询流程节点
	@RequestMapping(value = "saveNodeUser")
	@ResponseBody
	public AjaxRes saveNodeUser(NodeUserDto nodeUserDto) {
		User user = ShiroUtil.getCurrentUser();
		AjaxRes ar = getAjaxRes();
		// 删除
		NodeUserDto queryNodeUserDto = new NodeUserDto();
		queryNodeUserDto.setMerchantId(user.getMerchantId());
		// todo 设置项目ID
		queryNodeUserDto.setProjectId(nodeUserDto.getProjectId());
		queryNodeUserDto.setNodeId(nodeUserDto.getNodeId());
		ResponseList<NodeUser> nodeUserList = nodeUserServiceFacade.findByDto(queryNodeUserDto);
		if (nodeUserList.isSuccess() && null != nodeUserList.getEntity()) {
			for (NodeUser nu : nodeUserList.getEntity()) {
				nodeUserServiceFacade.deleteByPrimaryKey(nu.getId());
			}
		}
		if (null != nodeUserDto.getUserId()) {
			nodeUserDto.setMerchantId(user.getMerchantId());
			// todo 设置项目ID
			nodeUserDto.setProjectId(nodeUserDto.getProjectId());
			nodeUserDto.setCreateTime(new Date());
			nodeUserDto.setCreateUserId(user.getId());
			nodeUserDto.setDelFlag("0");
			nodeUserDto.setUpdateTime(new Date());
			Response<NodeUserDto> responseSave = nodeUserServiceFacade.save(nodeUserDto);
			if (responseSave.isSuccess()) {
				ar.setSucceed("");
			} else {
				ar.setFailMsg("保存失败");
			}
		} else {
			ar.setSucceed("");
		}
		return ar;
	}

	// 查询流程节点
	@RequestMapping(value = "queryProcessImages")
	@ResponseBody
	public List<Image> queryProcessImages(String objectId, String imageType) {
		User user = ShiroUtil.getCurrentUser();
		Long merchantId = 1L;
		if (null != user) {
			merchantId = user.getMerchantId();
		}
		return oaDataService.queryProcessImages(objectId, imageType, merchantId);
	}

}
